function rt_driver(configFile, T_interval, switches, sites, verbose)
    helpStr = [char(10)...
    'Script to drive rt suite' char(10)...
    char(10)...
    'Author: David Goldsmith, Wash. State Dept. of Ecology' char(10)...
    'dgol461@ecy.wa.gov' char(10)...
    'Release date: N/A' char(10)...
    char(10)...
    '************************** Usage **************************' char(10)...
    char(10)...
    'Typical:' char(10)...
    char(10)...
    '  From a DOS prompt (i.e., running the compiled .exe version)' char(10)... 
    char(10)...
    '    rt_driver "" "" "" "" (double quotes required)' char(10)... 
    char(10)...
    '  From the MATLAB command prompt' char(10)... 
    char(10)...
    '    rt_driver('''', ' ''''', ' ''''', ' ''''') (single quotes required)' char(10)... 
    char(10)...
    '  This "typical" usage results in the following default values being used:' char(10)... 
    char(10)...
    'configFile = '''', which results in default values being used for everything.' char(10)... 
    '             Specification of the (full, perhaps relative, path) name of a' char(10)... 
    '             text file provides an alternative to command line overriding' char(10)... 
    '             of defaults.' char(10)... 
    char(10)...
    '             Content format:' char(10)... 
    '               <parameter name>: <parameter value>' char(10)... 
    char(10)...
    '             Example (config.txt):' char(10)... 
    '               siteIDs: MCH01BR' char(10)... 
    '               siteIDs: MUK01BR' char(10)... 
    '               siteIDs: SQX01CF' char(10)... 
    '               siteIDs: WPA13CRN' char(10)... 
    '               siteIDs: MCH01SRN' char(10)... 
    '               mail_sender: dgol461@ecy.wa.gov' char(10)... 
    '               mail_recip: dgol461@ecy.wa.gov' char(10)... 
    '               mail_recip: brandon.sackmann@ecy.wa.gov' char(10)... 
    '               outgoingMailServ: 165.151.24.51' char(10)... 
    '               DB: Mooring_COTS_Dev' char(10)... 
    '               av_win: 1' char(10)... 
    '               av_thresh: 70' char(10)... 
    char(10)...
    ' Note: configFile = ''help'' displays this help string.' char(10)...
    char(10)...
    'T_interval = [(now - 14 days) now], where times are "serial date numbers"' char(10)... 
    '             (see MATLAB help for more information on sdn''s).' char(10)... 
    '             To specify a different interval, provide it as a string* in' char(10)... 
    '             the format [<sdn(start)> <sdn(stop)>] (square brackets and space' char(10)... 
    '             required) where <sdn(start)> (resp. stop) is the beginning' char(10)... 
    '             (resp. end) of the time interval as a serial date number.' char(10)... 
    char(10)...
    'switches   = ''ascii 1; plot 1; ftp 1; delete_plot 1; web 1; exit 1;''' char(10)... 
    '             where the value associated with:' char(10)... 
    '             ascii controls whether text output is generated;' char(10)... 
    '             plot controls whether graphic output is generated;' char(10)... 
    '             ftp controls the uploading of the results;' char(10)... 
    '             delete_plot controls whether the plots are closed (1) or' char(10)... 
    '               left open (0) after being rendered on the screen;' char(10)... 
    '             web tells the program whether (1) or not (0) the run is' char(10)... 
    '               for producing Web output; and' char(10)... 
    '             exit determines whether MATLAB is exited at completion.' char(10)... 
    char(10)...
    '             The default value of 1 for all of these produces all output,' char(10)... 
    '             uploads it all, destroys the plots after being rendered and saved,' char(10)... 
    '             and closes MATLAB at the end.' char(10)... 
    char(10)...
    '             To switch any of these to zero, include in a string argument its' char(10)... 
    '             identifier, a space, and then the digit 0, followed by a semi-colon' char(10)... 
    '             to separate multiple pairs.  For example, to turn "off" everything' char(10)... 
    '             but plot generation, include as the third argument (to rt_driver)' char(10)... 
    '             the string*:' char(10)... 
    char(10)...
    '             '' or "ascii 0; ftp 0; delete_plot 0; exit 0;'' or "' char(10)... 
    char(10)...
    '             (See end note about when to use '' vs. ")' char(10)... 
    char(10)...
    'sites      = ''MCH01BR; MUK01BR; SQX01CF; WPA13CRN; MCH01SRN; MUK01SRN;''' char(10)... 
    '             i.e., all telemetered mooring sites as of 9/19/2011.  (The' char(10)... 
    '             (required) N''s at the end of WPA and the two SR''s inform' char(10)... 
    '             the program that those sites are absent oxygen data; an N should' char(10)...
    '             also be appended to any future sites that also lack such data.)' char(10)... 
    char(10)...
    '             To specify a subset, simply remove the unwanted site(s) (including' char(10)... 
    '             their terminal N''s and semi-colons) from the above string and use' char(10)... 
    '             what remains as the fourth argument to rt_driver.' char(10)... 
    char(10)... 
    'verbose    = whether (1) or not (0) to report and log progress; default: 0' char(10)... 
    char(10)...
    '* To provide an argument as a string: from the MATLAB command prompt,' char(10)... 
    '  use single quotes, i.e., ''string'', but from a Windows (DOS) command' char(10)... 
    '  prompt, use double quotes, i.e., "string"'...
    ];
    
    if strcmp(configFile, 'help')
        disp(helpStr)
        return
    end
    if nargin < 5
        verbose = 0;
    end
    
    if strcmp(computer, 'PCWIN')
       cd '\\ecylcyfsvrxfile\eap\SHARED Files\MWDataMgmt\VSS_Mirror\executables\rt_driver32\distrib';
    else
       cd '\\ecylcyfsvrxfile\eap\SHARED Files\MWDataMgmt\VSS_Mirror\executables\rt_driver64\distrib';
    end
    
    log = 0;
    if verbose
      % Open log file
        logName = [pwd '\rt_logfile.txt'];
        [log, msg] = fopen(logName, 'wt');
        if isempty(msg) % if successful
            disp(['Log file ' logName ' successfully opened']);
            fprintf(log, ['RT_DRIVER LOG FILE ' datestr(utcnow()) '\n']);
            fprintf(log, 'Command line arguments:\n');
            fprintf(log, 'configFile: %s\n', configFile);
            fprintf(log, 'T_interval: %s\n', T_interval);
            fprintf(log, 'switches: %s\n', switches);
            fprintf(log, 'sites: %s', sites);
            fprintf(log, '\n');
        else
            disp('verbose set to true but couldn''t open log file.')
            disp('continuing (Ctrl-C to cancel)')
        end
    end
    
    sites = regexp(sites, ';\s|;', 'split');
    sites = sites(1:end-1);
    G = globals(configFile, T_interval, switches, sites, log);

  % For each site
    if G.logFile
        globals.log_progress(G,'\nEntering loop over sites\n')
    end
    for i=1:length(G.siteIDs)        
        G.siteID = G.siteIDs{i};
        if isequal(G.siteID(end), 'N')
            G.doDO = false;
            G.siteID = G.siteID(1:end-1);
        else
            G.doDO = true;
        end
        if G.logFile
            globals.log_progress(G,'Site: %s\nDoing dissolved oxygen (0=no, 1=yes): %u\n',...
                           {G.siteID, G.doDO});
        end
        
      % Get to or create the right directory and confirm
        localpath = [G.homeDir '\Output\' G.siteID];
        web_path = ['/staging/apps/eap/marinewq/moorings/' G.siteID '/'];
        switch G.siteID(1:3)
            case 'MCH'
                ftp_path = '/eap/Mooring_Raw/Puget_Sound/Manchester/';
            case 'MUK'
                ftp_path = '/eap/Mooring_Raw/Puget_Sound/Mukilteo/';
            case 'SQX'
                ftp_path = '/eap/Mooring_Raw/Puget_Sound/Squaxin/';
            case 'WPA'
                ftp_path = '/eap/Mooring_Raw/Willapa_Bay/Bay_Center_WPA13/';
        end
        if G.logFile
            globals.log_progress(G,'Output directory set to %s\n', {localpath});
            globals.log_progress(G,'Web output sub-path set to %s\n', {web_path});
            globals.log_progress(G,'FTP output sub-path set to %s\n', {ftp_path});
        end
        
        cdworked = 1; noArchive = 0;
        if isdir(localpath)
            cd(localpath);
        else
            mdsuccessful = mkdir(localpath);
            if mdsuccessful
                cd(localpath);
            else
                cdworked = 0;
            end
        end
        if cdworked
            if ~isdir([localpath '\Archive'])
                mdsuccessful = mkdir([localpath '\Archive']);
                if ~mdsuccessful 
                    noArchive = 1; 
                end
            end
            wd = localpath;
        end
        if G.logFile
            globals.log_progress(G,['Successfully cd-ed to output directory\n'...
                            'and confirmed presence of (or made) Archive sub-directory\n']);
        end
        
        fnameroot = [G.siteID, '_REALTIME'];
        if G.t_interval(1) == G.t_interval(2)-14
            datednameroot = [G.siteID datestr(G.t_interval(2), '_yyyymmdd')];
        else
            datednameroot = [G.siteID datestr(G.t_interval(1), '_ddmmmyyyy_HHMMSS')...
                             'to' datestr(G.t_interval(2), 'ddmmmyyyy_HHMMSS')];
        end
        if G.logFile
            globals.log_progress(G,'Constructed required file name roots:\n%s & %s\n',...
                           {fnameroot, datednameroot});
        end
        
        if i ~= length(G.siteIDs)
            G.tag = ['Continuing with ' G.siteIDs{i+1}];
        else
            G.tag = [G.siteID ' was the last site, ending'];
        end
        if G.logFile
            globals.log_progress(G,'Constructed site failure notification tag: %s\n',...
                           {G.tag});
            globals.log_progress(G,'Entering database queries section.\n')
        end
        
      % Get the data
        data = data_getter(G);
      % Unpack
        sitedata = data{1};
        if length(data) == 2
            [t, P, T, C, S, D, DOS, DO, RVID] = deal([]);
        else
            t = datenum(data{2});
            [P, T, C, S, D, DOS, DO, RVID] = deal(data{3:end});
        end
        
        if G.switches.web % If this run is to generate stuff for the Web
          % Censor data for presentation
            switch G.siteID
                case 'MCH01BR' 
                    remove = (P < 6.0) | (P > 18.0);
                case 'MUK01BR' 
                    remove = (P < 9.0) | (P > 19.0);
                case 'SQX01BR'
                    remove = (P < 3.0) | (P > 7.0);
                case 'SQX01CF'
                    remove = (P < 3.0) | (P > 7.0);
                case 'WPA13CR' 
                    remove = (P < 2.0) | (P > 10.0);
                otherwise 
                    remove = [];
            end
            P(remove) = NaN;
            T(remove) = NaN;
            if ~isempty(remove)
                remover = fix(remove);
                remover(remove) = NaN;
                D = D + remover;
                S = S + remover;
                C = C + remover;
            end
            DOS(remove) = NaN;
            if G.doDO
                DO(remove) = NaN;
            end
            T((T < 5 ) | (T > 20)) = NaN;
            if strcmp(G.siteID, 'WPA13CR')
                remove = (S < 10) | (S > 35);
            else
                remove = (S < 20) | (S > 35);
            end
            remover = fix(remove);
            remover(remove) = NaN;
            S = S + remover;
            D = D + remover;
            if G.doDO
                DO(DO < 3.0) = NaN;
            end
        end
        
      % Produce the ascii output
        if G.switches.ascii
            fname = writedat(G, fnameroot, sitedata, t, P, T, C, S, D, DOS, DO);
            criteria = {fname, 'Archive'};
            dlist = dir;
            try
                if all(ismember(criteria,{dlist.name})) % if successful and the Archive exists, copy file to Archive
                    [success, msg] = copyfile(fname, ['.\Archive\' fnameroot '.txt']);
                    if ~success
                        throw;
                    end
                else % Otherwise, report problem 
                    G.subj = 'RT error writing ascii output file, or Archive does not exist';
                    msg = fname;
                    throw;
                end
            catch ME
                sendmail(G.oEN.mail_recip, G.subj, msg);
            end
        end
        
      % Produce the plots
        if G.switches.plot
            if G.ftp_closed
                server = G.ftp_server;
                try
                    G.ftp_obj = ftp(server, 'eapuser', 'st*rd0g45');
                    G.ftp_closed = false;
                    cd(G.ftp_obj, '/eap/Mooring_Raw/');
                    capfile = mget(G.ftp_obj, 'WebCaption.txt');
                catch FE
                  % If communicating w/ the ftp server fails, notify and use the Y: drive
                    if ~G.ftp_failure
                        sendmail(G.oEN.mail_recip, 'RT Error Notification',...
                                 ['Error opening the ftp server trying to get caption.'...
                                  'Using caption available on the Y: drive']);
                        G.ftp_failure = true;
                    end
                    capfile = {[wd '\WebCaption.txt']};
                end
            end
            f = fopen(capfile{1});
            caption = fgetl(f);
            fclose(f);
            reqtext = struct('Title', sitedata(1:4), 'Caption', caption);
            fig = plotdat(G, t, P, T, S, D, DO, RVID, reqtext, {});
            fname = [fnameroot '.png'];
            set(fig.fh, 'InvertHardCopy', 'off');
            print(fig.fh, '-dpng', '-r150', fname);
            print(fig.fh, '-dpng', '-r150', ['.\Archive\' fnameroot '.png']);
            if G.switches.delete_plot
                delete(fig.fh);
            end
        end
        
      % Post the results
        if G.switches.ftp
            post_results(G, 'ftp', 'eapuser', 'E@rth1!', ftp_path, fnameroot);
            post_results(G, 'web', 'ecy\matlab', '$$?56jbM!', web_path, fnameroot);
        end
        
        clear *equipID* statID equipNom* UTCdeploy auxChan calibType resCol sID *calib* ...
              serialNoDesc equipTypeID manufacturer modelDesc form* unitsDesc;
    end
    if ~G.ftp_closed && ~G.ftp_failure
        close(G.ftp_obj)
    end
    if ~G.web_closed && ~G.web_failure
        close(G.web_obj)
    end
    if G.switches.exit || isdeployed
        exit force
    end
end

function post_results(G, host, user, p_wd, path, root)
  % Try to open ftp connections, upload files, and close connections
    eval(['server = G.' host '_server;']);
    eval(['closed = G.' host '_closed;']);
    eval(['failure = G.' host '_failure;']);
    if closed
        try
            eval(['G.' host '_obj = ftp(server, user, p_wd);']);
        catch FE
            if ~failure
                sendmail(G.oEN.mail_recip, 'RT Error Notification',...
                         ['Error opening ' host ' server trying to upload results.']);
                eval(['G.' host '_failure = true;']);
            end
        end
        eval(['G. ' host '_closed = false;']);
    end
    if ~eval(['G.' host '_failure'])
        eval(['cd(G.' host '_obj, path);']);
        switch host
            case 'ftp'
                eval(['mput(G.' host '_obj, [root ''*'']);']);
            case 'web'
                eval(['mput(G.' host '_obj, [''Archive\'' root ''.txt'']);']);
                eval(['mput(G.' host '_obj, [''Archive\'' root ''.png'']);']);
        end
    end
end